From: Owen W. Taylor Date: Mon, 20 Aug 2012 22:19:50 +0000 (-0400) Subject: pointerWatcher: add a class to track the pointer X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~6091 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=446583400e735e5a8acde3bddea731f0ee2f6e7c;p=gnome-shell.git pointerWatcher: add a class to track the pointer Add a class to efficiently poll the pointer position, stopping polling when the user is idle. https://bugzilla.gnome.org/show_bug.cgi?id=682310 --- diff --git a/js/Makefile.am b/js/Makefile.am index f574c5be5..b5bda7918 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -72,6 +72,7 @@ nobase_dist_js_DATA = \ ui/panel.js \ ui/panelMenu.js \ ui/placeDisplay.js \ + ui/pointerWatcher.js \ ui/polkitAuthenticationAgent.js \ ui/popupMenu.js \ ui/remoteSearch.js \ diff --git a/js/ui/pointerWatcher.js b/js/ui/pointerWatcher.js new file mode 100644 index 000000000..9c5d1fa44 --- /dev/null +++ b/js/ui/pointerWatcher.js @@ -0,0 +1,126 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const Lang = imports.lang; +const Mainloop = imports.mainloop; +const Shell = imports.gi.Shell; + +// We stop polling if the user is idle for more than this amount of time +const IDLE_TIME = 1000; + +// This file implements a reasonably efficient system for tracking the position +// of the mouse pointer. We simply query the pointer from the X server in a loop, +// but we turn off the polling when the user is idle. + +let _pointerWatcher = null; +function getPointerWatcher() { + if (_pointerWatcher == null) + _pointerWatcher = new PointerWatcher(); + + return _pointerWatcher; +} + +const PointerWatch = new Lang.Class({ + Name: 'PointerWatch', + + _init: function(watcher, interval, callback) { + this.watcher = watcher; + this.interval = interval; + this.callback = callback; + }, + + // remove: + // remove this watch. This function may safely be called + // while the callback is executing. + remove: function() { + this.watcher._removeWatch(this); + } +}); + +const PointerWatcher = new Lang.Class({ + Name: 'PointerWatcher', + + _init: function() { + let idleMonitor = Shell.IdleMonitor.get(); + idleMonitor.add_watch(IDLE_TIME, + Lang.bind(this, this._onIdleMonitorWatch)); + this._idle = idleMonitor.get_idletime() > IDLE_TIME; + this._watches = []; + this.pointerX = null; + this.pointerY = null; + }, + + // addWatch: + // @interval: hint as to the time resolution needed. When the user is + // not idle, the position of the pointer will be queried at least + // once every this many milliseconds. + // @callback: function to call when the pointer position changes - takes + // two arguments, X and Y. + // + // Set up a watch on the position of the mouse pointer. Returns a + // PointerWatch object which has a remove() method to remove the watch. + addWatch: function(interval, callback) { + // Avoid unreliably calling the watch for the current position + this._updatePointer(); + + let watch = new PointerWatch(this, interval, callback); + this._watches.push(watch); + this._updateTimeout(); + return watch; + }, + + _removeWatch: function(watch) { + for (let i = 0; i < this._watches.length; i++) { + if (this._watches[i] == watch) { + this._watches.splice(i, 1); + this._updateTimeout(); + return; + } + } + }, + + _onIdleMonitorWatch: function(monitor, id, userBecameIdle) { + this._idle = userBecameIdle; + if (!userBecameIdle) + this._updatePointer(); + + this._updateTimeout(); + }, + + _updateTimeout: function() { + if (this._timeoutId) { + Mainloop.source_remove(this._timeoutId); + this._timeoutId = 0; + } + + if (this._idle || this._watches.length == 0) + return; + + let minInterval = this._watches[0].interval; + for (let i = 1; i < this._watches.length; i++) + minInterval = Math.min(this._watches[i].interval, minInterval); + + this._timeoutId = Mainloop.timeout_add(minInterval, + Lang.bind(this, this._onTimeout)); + }, + + _onTimeout: function() { + this._updatePointer(); + return true; + }, + + _updatePointer: function() { + let [x, y, mods] = global.get_pointer(); + if (this.pointerX == x && this.pointerY == y) + return; + + this.pointerX = x; + this.pointerY = y; + + for (let i = 0; i < this._watches.length;) { + let watch = this._watches[i]; + watch.callback(x, y); + if (watch == this._watches[i]) // guard against self-removal + i++; + } + } +});